package com.hulk.boot;

import com.hulk.rpc.annotation.HulkFailover;
import com.hulk.rpc.annotation.HulkService;
import com.hulk.rpc.config.common.ParseUtils;
import com.hulk.rpc.config.server.ServerConfig;
import com.hulk.rpc.invoke.ServiceInvokeFactory;
import com.hulk.rpc.server.HulkServer;
import com.hulk.rpc.utils.tuple.Tuple2;
import com.hulk.rpc.utils.tuple.Tuple3;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.hulk.rpc.utils.tuple.Tuple.tuple;

@Configuration
@ConditionalOnClass({HulkService.class, EnableHulkServer.class})
@Order
@Slf4j
public class HulkServerStarter {
  @Autowired
  private GenericApplicationContext applicationContext;

  private HulkServer hulkServer;

  @PreDestroy
  public void close() {
    if (hulkServer == null) {
      return;
    }

    try {
      hulkServer.close();
    } catch (Throwable e) {
      if (log.isErrorEnabled()) {
        log.error("HulkServer关闭失败!", e);
      }
    }
  }

  @PostConstruct
  public void startHulkServer() {

    ServerConfig serverConfig;
    try {
      serverConfig = ParseUtils.getInstance().parseServerConfig("hulk-server.conf");
    } catch (com.typesafe.config.ConfigException configException) {
      if (log.isErrorEnabled()) {
        log.error("hulk-server.conf 格式错误，无法开启HulkServer!", configException);
      }

      return;
    } catch (Throwable e) {
      if (log.isErrorEnabled()) {
        log.error("类路径中找不到 hulk-server.conf，无法开启HulkServer!", e);
      }

      return;
    }

    @SuppressWarnings("rawtypes")
    Collection<Tuple3<HulkService, Class, Object>> hulkServiceList = getHulkServiceList();

    if (hulkServiceList.isEmpty()) {
      if (log.isErrorEnabled()) {
        log.error("找不到有效的 HulkService，无法开启HulkServer!");
      }

      return;
    }

    ServiceInvokeFactory invokerFactory = new ServiceInvokeFactory(serverConfig.getGroup(), serverConfig.getApp());
    hulkServiceList.forEach(t3 -> {
      invokerFactory.register(t3._2, t3._3);
    });

    try {
      hulkServer = new HulkServer(serverConfig, invokerFactory);
      hulkServer.startAndRegisterServer();

      Map<String, HulkServerAware> hulkServerAwareMap//
              = applicationContext.getBeansOfType(HulkServerAware.class);

      hulkServerAwareMap.forEach((key, value) -> value.setHulkServer(hulkServer));
    } catch (Throwable e) {
      if (log.isErrorEnabled()) {
        log.error("HulkServer启动失败!", e);
      }
    }
  }

  @SuppressWarnings("rawtypes")
  private Collection<Tuple3<HulkService, Class, Object>> getHulkServiceList() {
    Map<String, Object> beans = applicationContext.getBeansOfType(Object.class);

    if (beans.isEmpty()) {
      return List.of();
    }

    Map<Class, Tuple3<HulkService, Class, Object>> hulkServiceMap = beans//
            .entrySet()//
            .parallelStream()//
            .map(kv -> {
              Object bean = kv.getValue();
              Tuple2<HulkService, Class> t2 = getHulkService(bean);

              if (t2 == null) {
                return null;
              }

              if (log.isDebugEnabled()) {
                log.debug("find HulkService: " + kv.getKey() + " " + t2._2.getName() + t2._1);
              }

              return tuple(t2._1, t2._2, bean);
            })//
            .filter(Objects::nonNull)//
            .collect(Collectors.toConcurrentMap(//
                    t3 -> t3._2, //
                    t3 -> t3, //
                    (Tuple3<HulkService, Class, Object> v1, Tuple3<HulkService, Class, Object> v2) -> {
                      if (log.isWarnEnabled()) {
                        HulkService hulkService = v1._1;
                        Class hulkServiceClass = v1._2;

                        Class implClass1 = v1._3.getClass();
                        Class implClass2 = v2._3.getClass();

                        log.warn("存在冲突 HulkService: " + hulkServiceClass.getName() + hulkService //
                                + ", 生效: " + implClass1.getName() //
                                + ", 忽略: " + implClass2.getName());
                      }

                      return v1;
                    }));

    return hulkServiceMap.values();
  }

  @SuppressWarnings("rawtypes")
  private Tuple2<HulkService, Class> getHulkService(Object bean) {
    if (bean == null) {
      return null;
    }

    Class<?> beanClass = bean.getClass();

    if (beanClass.getAnnotation(HulkFailover.class) != null) {
      return null;
    }

    Class<?>[] interfaces = beanClass.getInterfaces();
    if (interfaces == null || interfaces.length == 0) {
      return null;
    }

    for (Class<?> interfaceClass : interfaces) {
      HulkService hulkService = interfaceClass.getAnnotation(HulkService.class);
      if (hulkService != null) {
        return tuple(hulkService, interfaceClass);
      }
    }

    return null;
  }

}
